#include "lib.h"
#include "filestream.h"
#include "hashtable.h"
#include "rfile.h"
#include "rentry.h"
#include "rresource.h"
#include "stringcomparator.h"
using namespace Library;


//----------------------------------------------------------------------------
// Public
//
ResourceFile::ResourceFile()
{
	m_stream	= 0;
	m_entries	= new HashTable<pResourceEntry>;
}


ResourceFile::~ResourceFile()
{
	// Delete the resource entries.
	m_entries->vaporize();

	delete m_entries;
	m_entries = 0;

	delete m_stream;
	m_stream = 0;
}

bool8
ResourceFile::open(const String& filename)
{
	pFileStream fs = new FileStream();

	if (!fs || 
		fs->open(filename, FileStream::reading) == false)
	{
		delete fs;
		fs = 0;

		return false;
	}

	return open(fs);
}

bool8
ResourceFile::open(Stream * stream)
{
	Header			header;
	Index *			index = 0;
	uint32			index_loop;
	uint32			index_size;
	uint32			entry_loop;
	pResourceEntry	entry;
	bool8			rv;

	// One file open at a time please...
	if (close() == false)
		return false;

	m_stream = stream;

	// Open and read the file header.
	if (m_stream->read(&header, sizeof Header) != sizeof Header)
		goto abort;

	// Ensure we are working with a resource file.
	if (strcmp(RF_MAGIC, (const char *)header.m_magic))
		goto abort;

	index_size	= sizeof Index * header.m_indices;
	index		= new Index[ index_size ];
	if (!index)
		goto abort;

	if (m_stream->seek(header.m_offset, Stream::beginning) != (int32)header.m_offset ||
		m_stream->read(index, index_size) != (int32)index_size)
		goto abort;

	for (index_loop = 0; index_loop < header.m_indices; index_loop++)
	{
		if (m_stream->seek(index[index_loop].m_offset, Stream::beginning) != (int32)index[index_loop].m_offset)
			goto abort;

		for (entry_loop = 0; entry_loop < index[index_loop].m_entries; entry_loop++)
		{
			entry = createResourceEntry();
			if (!entry || !entry->load(m_stream, header.m_dif_entry))
				goto abort;

			if (!m_entries->add(entry->name(), entry))
				goto abort;
		}
	}

	// Wait until after all the hard work has finished before calling archive.
	if (archive(&header) == false)
		goto abort;

	rv = true;
	goto done;

abort:
	close();
	rv = false;
	goto done;

done:
	delete [] index;

	return rv;
}


bool8
ResourceFile::close()
{
	// Delete the resource entries.
	m_entries->vaporize();

	delete m_stream;
	m_stream = 0;

	return true;
}


void *
ResourceFile::get(const String & name)
{
	pResourceEntry	entry;

	return m_entries->get(name, &entry) ? entry->acquire() : 0;
}


// kharmon.  01-09-07.  Adding ability to decompile rsc files.
// Basically, I need to know the length of the data buffer I'm being handed back.  So, added this overload
// of the get method, so as not to disturb all of the existing code that uses the other get method.
void *
ResourceFile::get(const String & name, uint32 *punDataLength)
{
    void *pvData = NULL;

    if(punDataLength!=NULL)
    {
        *punDataLength = 0;
    }

	pResourceEntry	entry;

	if(m_entries->get(name, &entry)) 
    {
        pvData = entry->acquire();

        if(pvData!=NULL && punDataLength!=NULL)
        {            
            *punDataLength = entry->size();
        }
    }

    return pvData;
}



bool8
ResourceFile::release(void * resource)
{
	return Resource::release(resource);
}


uint32
ResourceFile::resourceCount()
{
	return m_entries->size();
}


pResourceFileList
ResourceFile::resourceList()
{
	HashTableIterator<pResourceEntry>	iterator(m_entries);
	pResourceFileList					list = 0;
	StringComparator *					comparator;

	// ResourceFileList needs a pointer to delete when finished.
	comparator = new StringComparator();
	if (comparator)
	{
		list = new ResourceFileList(comparator);
		if (list)
		{
			for (iterator.begin(); iterator.key(); iterator.operator++(0))
				list->add(iterator.key());
		}
	}

	return list;
}


//----------------------------------------------------------------------------
//
//
bool8
ResourceFile::archive(Header * header)
{
	// Do nothing!  This is a lightweight class whose sole responsibilty is
	// delivering resource content to the client.  Archiving *any* data here
	// for later use, especially if it's for a sub-class, munges the intended
	// object model.
	//
	// If you need header data, sub-class ResourceFile and override this
	// method.
	return true;
}

pResourceEntry
ResourceFile::createResourceEntry()
{
	return new ResourceEntry();
}


//
// vim: nowrap
//
